/*
 * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND 
 * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER.
 * CONSEQUENTLY, SEMTECH SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR
 * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT
 * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION
 * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
 * 
 * Copyright (C) SEMTECH S.A.
 */
/*! 
 * \file       radio.c
 * \brief      Generic radio driver ( radio abstraction )
 *
 * \version    2.0.0 
 * \date       Nov 21 2012
 * \author     Miguel Luis
 *
 * Last modified by Gregory Cristian on Apr 25 2013
 */

#include "stdio.h"
#include "radio.h"
#include "sx1276.h"
#include "transfer.h"
#include "sx127x-board.h"
#include "sx127x_dio.h"
#include "sx127x_timer.h"

#include <pthread.h>  
#include <string.h>
#include "unistd.h"
#include "semaphore.h"
/*******************************************************************************
 * MACROS
 */
#define LORA_MAC_PRIVATE_SYNCWORD                   0x12
#define LORA_MAC_PUBLIC_SYNCWORD                    0x34



/*******************************************************************************
 * LOCAL FUNCTIONS DECLEAR
 */
static void rfStartRx( void );

static void TxDone( void );

static void TxTimeout( void );

static void RxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );

static void RxTimeout( void );

static void RxError ( void );

static void FhssChangeChannel( uint8_t currentChannel );

static void CadDone( bool channelActivityDetected );

static void radio_timer_thread(void);

static void radio_dio_thread(void);
/*******************************************************************************
 * GLOBAL VARIABLES
 */


/*******************************************************************************
 * STATIC VARIABLES
 */
static pthread_t radio_timer_thrid;
static pthread_t radio_dio_thrid;

static sem_t sem_recv_ind;

RadioEvents_t tRadioEvents = {
        TxDone,
        TxTimeout,
        RxDone,
        RxTimeout,
        RxError,
        FhssChangeChannel,
        CadDone
};

rfPara_t s_tRfPara = {
        .ulFreq = 472100000,
        .ulFdev = 0,
        .bandwidthAfc = 0,
        .cPwr = 20,
        .ucBw = 7,//125k
        .ucSf = 7,//sf7
        .ucCodingRate = 1,
        .usPreamble = 8,
        .usWakeTime = 100,
        .iqInverted = 0,
        .ucCrc = 1,
        .ucFhss = 0,
        .ucModem = 1
};




rfRxPkt_t sx1276_rx;
int debug = -1;
/*****************************************/
/*
 * LOCAL FUNCTIONS
!*/
static void SetPublicNetwork( bool enable )
{
    //PublicNetwork = enable;
    Radio.SetModem( MODEM_LORA );
    if( enable == true )
    {
        // Change LoRa modem SyncWord
        Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD );
    }
    else
    {
        // Change LoRa modem SyncWord
        Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD );
    }

    printf("sync = %02x. \n", Radio.Read( REG_LR_SYNCWORD ));
   
}


void radio_params_config( rfPara_t *a_tPara )
{
    if(a_tPara == NULL)
        return;
    
    memcpy( &s_tRfPara, a_tPara, sizeof( s_tRfPara ));  
}

void rf_setMode( uint8_t a_ucRfMode )
{
    switch ( a_ucRfMode )
    {
    case RF_MODE_NORMAL:
        /* set in receive mode */
        rfStartRx();
        break;
    case RF_MODE_WAKE:
        /* set in receive mode */

        Radio.Rx( 0 );
        break;
    case RF_MODE_CAD:
        /* set in receive mode */
        Radio.StartCad( );
        break;
    case RF_MODE_SLEEP:
        Radio.Sleep();
        break;
    case RF_MODE_DEBUG:
        debug = 0;
        break;
    default:
        break;
    }
}


void rfStartRx( void )
{
    if(debug == 0)
        radio_sx127x_start_rx();
}

int cnt = 0;
void rf_send( uint8_t *a_pucData, uint16_t a_usLen )
{
    //s_tRfPara.ucCrc = 1; 
   // s_tRfPara.iqInverted = 0;
    //uint8_t flags;
    
		SX1276SetSleep();
		SX1276SetStby();

		SX1276SetChannel( s_tRfPara.ulFreq);
		printf("set channel :%d\r\n",s_tRfPara.ulFreq);
		
        Radio.SetTxConfig( (RadioModems_t)s_tRfPara.ucModem, s_tRfPara.cPwr, s_tRfPara.ulFdev,
                                  s_tRfPara.ucBw, s_tRfPara.ucSf,
                                  s_tRfPara.ucCodingRate, s_tRfPara.usPreamble,
                                  false, s_tRfPara.ucCrc == 0 ? false : true, s_tRfPara.ucFhss == 0 ? false : true, 4, s_tRfPara.iqInverted, 1000000);

        Radio.Send( a_pucData, a_usLen );

}

void rf_sleep( void )
{
    Radio.Sleep();
}

void TxDone( void )
{
    //printf("tx done.\n");
    rfStartRx();
}


void TxTimeout( void )
{
    printf("tx timeout.\n");
    //printf("+++[t-tmo]OP-MODE : %02x .\n",SX1276Read(REG_OPMODE));
    rfStartRx();
}

void RxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
    //uint8_t aucRssiSnr[3];
    //uint16_t len;

    memset(&sx1276_rx, 0, sizeof(rfRxPkt_t));
    memcpy(sx1276_rx.payload, payload, size);
    sx1276_rx.size = size;
    sx1276_rx.rssi = rssi;
    sx1276_rx.snr = snr;
    sem_post(&sem_recv_ind);
    rfStartRx();
}

void RxTimeout( void )
{
    printf("rx time out.\n");
    rfStartRx();
}

void RxError ( void )
{
    //printf("crc error.\n");
    rfStartRx();
}

void FhssChangeChannel( uint8_t currentChannel )
{
    currentChannel = currentChannel;
}

void CadDone( bool channelActivityDetected )
{
    if ( true == channelActivityDetected ){
        Radio.Rx( 0 );
    }else{
        Radio.Rx( 0 );
        //Radio.Sleep();
        /* TODO: set wake time event */
    }
}




void radio_sx127x_init(void)
{   
    int i;

    /* init */
    i = sem_init(&sem_recv_ind, 0, 0);
    if (i != 0){
        printf("semaphore sem_recv_ind initialization failed. \n");
    }  
    
    SX1276BoardInit();
    Radio.Init( &tRadioEvents );
    SetPublicNetwork(true);
    Radio.SetChannel( s_tRfPara.ulFreq ); 
    printf("version = %02x. \n", Radio.Read( REG_LR_VERSION ));
    //printf("addr-44 = %02x. \n", Radio.Read( 0x44 ));

    //printf("addr-06 = %02x. \n", Radio.Read( 0x06 ));
    //printf("addr-07 = %02x. \n", Radio.Read( 0x07 ));
    //printf("addr-08 = %02x. \n", Radio.Read( 0x08 ));
    i = pthread_create(&radio_timer_thrid, NULL,(void *(*)(void*))radio_timer_thread, NULL); 
    if(i != 0){
        printf("radio_timer_thread create failed.\n");
    }
    i = pthread_create(&radio_dio_thrid, NULL,(void *(*)(void*))radio_dio_thread, NULL); 
    if(i != 0){
        printf("radio_dio_thread create failed.\n");
    }

    //radio_sx127x_start_rx();
    
}


void radio_sx127x_send(uint32_t freq, uint8_t *buf, uint16_t size)
{
    uint32_t frequency;
if(debug < 0){    
    s_tRfPara.ucCrc = 1; 
    s_tRfPara.iqInverted = 0;
}
    if((freq < MIN_FREQ) || (freq > MAX_FREQ)){
        frequency = s_tRfPara.ulFreq;
    }else{
        frequency = freq;
    }
    SX1276SetChannel(frequency);

    //Radio.SetTxConfig( MODEM_LORA, 20, 0, 0, 8, 1, 8, false, true, 0, 0, false, 3e3 );
    //Radio.SetTxConfig( MODEM_LORA, 20, 0, 7, 8, 1, 8, false, true, 0, 0, false, 3e3 );
    //Radio.Send( buf, size );
#if 1
    Radio.SetTxConfig( (RadioModems_t)s_tRfPara.ucModem, s_tRfPara.cPwr, s_tRfPara.ulFdev,
                              s_tRfPara.ucBw, s_tRfPara.ucSf,
                              s_tRfPara.ucCodingRate, s_tRfPara.usPreamble,
                              false, s_tRfPara.ucCrc == 0 ? false : true, s_tRfPara.ucFhss == 0 ? false : true, 4, s_tRfPara.iqInverted, 3e3);

    Radio.Send( buf, size );
#endif
}



int radio_sx127x_receive(rfRxPkt_t *pkt)
{
    if(0 > sem_wait(&sem_recv_ind)){
        return -1;
    }
    memcpy(pkt, &sx1276_rx, sizeof(rfRxPkt_t));
    return 0;
}


void radio_sx127x_start_rx(void)
{
    if(debug < 0){
        s_tRfPara.ucCrc = 0;
        s_tRfPara.iqInverted = 1;
    }
    SX1276SetChannel(s_tRfPara.ulFreq);
    printf("rx freq : %d \n", s_tRfPara.ulFreq);
    Radio.SetRxConfig((RadioModems_t)s_tRfPara.ucModem, s_tRfPara.ucBw, s_tRfPara.ucSf, \
                s_tRfPara.ucCodingRate, s_tRfPara.bandwidthAfc, s_tRfPara.usPreamble, 1023, \
                false, 255, s_tRfPara.ucCrc == 0 ? false : true, s_tRfPara.ucFhss == 0 ? false : true, 4, s_tRfPara.iqInverted, true );

    Radio.Rx( 0 );
}


uint32_t radio_sx127x_get_time_on_air(uint8_t pkt_len)
{
    return Radio.TimeOnAir(MODEM_LORA, pkt_len);
}





void radio_timer_thread(void)
{  
    while(1){
        TimerIrqHandler();
        TimerLowPowerHandler();
        usleep(1000);
    } 
}



void radio_dio_thread(void)
{
    while(1){
        if((RFLR_IRQFLAGS_TXDONE & Radio.Read(REG_LR_IRQFLAGS)) != 0){
            sx1278Dio0_Isr();
        }
 
        if((RFLR_IRQFLAGS_RXTIMEOUT & Radio.Read(REG_LR_IRQFLAGS)) != 0){
            sx1278Dio1_Isr();
        }  

        if((RFLR_IRQFLAGS_PAYLOADCRCERROR & Radio.Read(REG_LR_IRQFLAGS)) != 0){
            sx1278Dio0_Isr();
        }

        if((RFLR_IRQFLAGS_RXDONE & Radio.Read(REG_LR_IRQFLAGS)) != 0){
            sx1278Dio0_Isr();
        }
        
        usleep(1000);
    }
}




